/**
 * Copyright 2018-2019 jianggujin (www.jianggujin.com).
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jianggujin.modulelink.module;

import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import com.jianggujin.modulelink.JModule;
import com.jianggujin.modulelink.config.JMultiVersionModuleConfig;
import com.jianggujin.modulelink.exception.JModuleLinkException;
import com.jianggujin.modulelink.exception.JModuleNotFoundException;
import com.jianggujin.modulelink.util.JAssert;
import com.jianggujin.modulelink.util.JModuleUtils;
import com.jianggujin.modulelink.util.JStringUtils;

/**
 * 运行的模块
 * 
 * @author jianggujin
 *
 */
public class JMultiVersionModule {
    private final String moduleName;
    private JModule defaultModule = null;
    private Map<String, JModule> modules = new ConcurrentHashMap<String, JModule>();

    public JMultiVersionModule(String moduleName) {
        JAssert.checkNotNull(moduleName, "module name must not be null");
        this.moduleName = moduleName;
    }

    public JMultiVersionModule(JModule module) {
        JAssert.checkNotNull(module, "module must not be null");
        JMultiVersionModuleConfig moduleConfig = (JMultiVersionModuleConfig) module.getModuleConfig();
        this.moduleName = moduleConfig.getModuleName();
        this.modules.put(moduleConfig.getVersion(), module);
        this.setDefaultModule(module);
    }

    /**
     * 获得指定版本模块
     * 
     * @param version 模块版本
     * @return 模块对象
     * @throws JModuleNotFoundException 未找到模块异常
     */
    public JModule getModule(String version) {
        JModule module = optModule(version);
        if (module == null) {
            throw new JModuleNotFoundException(
                    "could not found module with module name:" + moduleName + ", version:" + version);
        }
        return module;

    }

    /**
     * 获得指定版本模块
     * 
     * @param version 模块版本
     * @return 不存在则返回null
     */
    public JModule optModule(String version) {
        return this.modules.get(version);
    }

    /**
     * 判断是否有指定版本
     * 
     * @param version 模块版本
     * @return 存在则返回true
     */
    public boolean hasVersion(String version) {
        if (version == null) {
            return false;
        }
        return this.modules.containsKey(version);
    }

    /**
     * 获得默认模块
     * 
     * @return 默认模块
     * @throws JModuleNotFoundException 未找到模块异常
     */
    public JModule getDefaultModule() {
        JModule module = optDefaultModule();
        if (module == null) {
            throw new JModuleNotFoundException("could not found default module with module name:" + moduleName);
        }
        return module;
    }

    /**
     * 获得默认模块
     * 
     * @return 不存在则返回null
     */
    public JModule optDefaultModule() {
        return this.defaultModule;
    }

    /**
     * 添加模块
     * 
     * @param module 需要添加的模块
     */
    public void addModule(JModule module) {
        JMultiVersionModuleConfig moduleConfig = (JMultiVersionModuleConfig) module.getModuleConfig();
        String moduleName = moduleConfig.getModuleName(), version = moduleConfig.getVersion();
        JAssert.checkState(moduleName.equals(this.moduleName),
                "module name not match, add module name:%s, current module name:%s", moduleName, this.moduleName);
        modules.put(version, module);
        if (moduleConfig.isActive()) {
            this.setDefaultModule(module);
        }
    }

    /**
     * 获得模块名称
     * 
     * @return 模块名称
     */
    public String getModuleName() {
        return moduleName;
    }

    /**
     * 设置默认版本
     * 
     * @param defaultVersion 默认版本
     */
    public void setDefaultVersion(String defaultVersion) {
        setDefaultModule(getModule(defaultVersion));
    }

    /**
     * 设置默认模块
     * 
     * @param module
     */
    private synchronized void setDefaultModule(JModule module) {
        if (this.defaultModule != null) {
            if (this.defaultModule instanceof JDefaultModule) {
                ((JDefaultModule) this.defaultModule).publishUnActivedEvent();
            }
        }
        this.defaultModule = module;
        if (module instanceof JDefaultModule) {
            ((JDefaultModule) module).publishActivedEvent();
        }
    }

    /**
     * 设置默认版本
     * 
     * @param defaultVersion 默认版本
     * @return {@link JMultiVersionModule}
     */
    public JMultiVersionModule withDefaultVersion(String defaultVersion) {
        setDefaultVersion(defaultVersion);
        return this;
    }

    /**
     * 销毁指定版本
     * 
     * @param version 模块版本
     * @return 销毁后是否为空
     */
    public boolean destory(String version) {
        // 需要销毁的版本为默认版本并且当前模块存在2个以上，销毁默认版本后无法确认新的默认版本号
        if (modules.size() > 2 && JStringUtils.equals(version, getDefaultVersion())) {
            throw new JModuleLinkException("module has version more than 2, default version could not allow remove.");
        }
        JModule module = modules.remove(version);
        if (module == null) {
            throw new JModuleNotFoundException(
                    "could not found module with module name:" + moduleName + ", version:" + version);
        }
        JModuleUtils.destroyQuietly(module);
        // 如果还剩一个模块，则更换moduleObj的值
        if (modules.size() == 1) {
            this.setDefaultModule(modules.entrySet().iterator().next().getValue());
        }
        return this.modules.isEmpty();
    }

    /**
     * 获得默认版本
     * 
     * @return 默认版本
     */
    public String getDefaultVersion() {
        return this.defaultModule != null
                ? ((JMultiVersionModuleConfig) this.defaultModule.getModuleConfig()).getVersion()
                : null;
    }

    /**
     * 获得所有模块
     * 
     * @return
     */
    public Collection<JModule> getModules() {
        return this.modules.values();
    }

    /**
     * 获得所有版本
     * 
     * @return
     */
    public Set<String> getVersions() {
        return this.modules.keySet();
    }

    /**
     * 销毁
     * 
     */
    public void destory() {
        Iterator<Entry<String, JModule>> iterator = this.modules.entrySet().iterator();
        while (iterator.hasNext()) {
            Entry<String, JModule> entry = iterator.next();
            JModuleUtils.destroyQuietly(entry.getValue());
            iterator.remove();
        }
    }

    /**
     * 判断是否为空
     * 
     * @return 不存在模块则返回true
     */
    public boolean isEmpty() {
        return this.modules.isEmpty();
    }
}
